package gu.simplemq.jms;

import java.net.URI;
import java.util.Map;
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.JMSException;

import org.apache.activemq.jms.pool.PooledConnectionFactory;

import gu.simplemq.pool.BaseMQPool;
import gu.simplemq.utils.IntrospectionSupport;
/**
 * 延迟初始化的 {@link Connection}资源池（线程安全）,使用方法:<br>
 * 通过 {@link #getDefaultInstance()} 和getInstance(...)系列静态方法获取{@link JmsPoolLazy}实例<br>
 * 通过{@link #apply()} 和 {@link #free()}方法实现{@link Connection}对象的申请和释放
 * @author guyadong
 *
 */
public class JmsPoolLazy extends BaseMQPool<Connection> implements JmsConstants{
	private final Properties parameters;	
	private volatile PooledConnectionFactory pool;	
	private final URI location;
	private String username;
	private String password;
	final JmsPoolLazys jmsPoolLazys;
	final JmsRuntimeContext runtimeContext;
	@SuppressWarnings("rawtypes")
	protected JmsPoolLazy (Map props, JmsPoolLazys jmsPoolLazys) {
		this.jmsPoolLazys = jmsPoolLazys;
		this.runtimeContext = jmsPoolLazys.runtimeContext;
		parameters = runtimeContext.getPropertiesHelper().initParameters(props);
		location =  runtimeContext.getPropertiesHelper().getLocationlURI(props);
		jmsPoolLazys.POOLS.put(location,this);
		// set field value for username,password
		// 这里不能用归一化的parameters变量，而要用原始输入的props用于初始化 username,password字段
		// 因为initParameters方法会修改props中的字段
		IntrospectionSupport.setProperties(this, props,false );
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * 将当前实例指定为默认实例
	 * @return
	 * @see #setDefaultInstance(JmsPoolLazy)
	 */
	public JmsPoolLazy asDefaultInstance(){
		jmsPoolLazys.setDefaultInstance(this);
		return this;
	}
	private PooledConnectionFactory createPool(){
		PooledConnectionFactory pool = runtimeContext.createPooledConnectionFactory(parameters);
		logger.info("jms pool initialized(连接池初始化)  {} ",getCanonicalURI());
		return pool;
	}

	@Override
	public Connection borrow(){
		// double-checked locking
		if(null == pool){
			synchronized (this){
				if(null == pool){
					pool = createPool();
				}
			}
		}
        try {
			return pool.createConnection(username,password);
		} catch (JMSException e) {
			throw new MQPoolException(e);
		}
    }
    
	@Override
    public void release(Connection r) {
        if (r != null){
            try {
				r.close();
			} catch (JMSException e) {
				throw new RuntimeException(e);
			}
        }
    }
	@Override
	public URI getCanonicalURI(){
		return  runtimeContext.getPropertiesHelper().getLocationlURI(parameters);
	}
	
	@Override
	public void close(){
		// double check
		if(pool != null){
			synchronized (this){
				if(pool != null){
					pool.stop();
					jmsPoolLazys.POOLS.remove(location);
					logger.info("discard jms pool: {}",this);
					pool = null;					
					closed = true;
				}
			}
		}
	}
}
